home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2000 March / maximum-cd-2000-03.iso / Quake3 Game Source / Q3AGameSource.exe / Main / q_shared.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-01-18  |  16.4 KB  |  985 lines

  1. // Copyright (C) 1999-2000 Id Software, Inc.
  2. //
  3. // q_shared.c -- stateless support routines that are included in each code dll
  4. #include "q_shared.h"
  5.  
  6. float Com_Clamp( float min, float max, float value ) {
  7.     if ( value < min ) {
  8.         return min;
  9.     }
  10.     if ( value > max ) {
  11.         return max;
  12.     }
  13.     return value;
  14. }
  15.  
  16.  
  17. /*
  18. ============
  19. COM_SkipPath
  20. ============
  21. */
  22. char *COM_SkipPath (char *pathname)
  23. {
  24.     char    *last;
  25.     
  26.     last = pathname;
  27.     while (*pathname)
  28.     {
  29.         if (*pathname=='/')
  30.             last = pathname+1;
  31.         pathname++;
  32.     }
  33.     return last;
  34. }
  35.  
  36. /*
  37. ============
  38. COM_StripExtension
  39. ============
  40. */
  41. void COM_StripExtension( const char *in, char *out ) {
  42.     while ( *in && *in != '.' ) {
  43.         *out++ = *in++;
  44.     }
  45.     *out = 0;
  46. }
  47.  
  48.  
  49. /*
  50. ==================
  51. COM_DefaultExtension
  52. ==================
  53. */
  54. void COM_DefaultExtension (char *path, int maxSize, const char *extension ) {
  55.     char    oldPath[MAX_QPATH];
  56.     char    *src;
  57.  
  58. //
  59. // if path doesn't have a .EXT, append extension
  60. // (extension should include the .)
  61. //
  62.     src = path + strlen(path) - 1;
  63.  
  64.     while (*src != '/' && src != path) {
  65.         if ( *src == '.' ) {
  66.             return;                 // it has an extension
  67.         }
  68.         src--;
  69.     }
  70.  
  71.     Q_strncpyz( oldPath, path, sizeof( oldPath ) );
  72.     Com_sprintf( path, maxSize, "%s%s", oldPath, extension );
  73. }
  74.  
  75. /*
  76. ============================================================================
  77.  
  78.                     BYTE ORDER FUNCTIONS
  79.  
  80. ============================================================================
  81. */
  82.  
  83. // can't just use function pointers, or dll linkage can
  84. // mess up when qcommon is included in multiple places
  85. static short    (*_BigShort) (short l);
  86. static short    (*_LittleShort) (short l);
  87. static int        (*_BigLong) (int l);
  88. static int        (*_LittleLong) (int l);
  89. static float    (*_BigFloat) (float l);
  90. static float    (*_LittleFloat) (float l);
  91.  
  92. short    BigShort(short l){return _BigShort(l);}
  93. short    LittleShort(short l) {return _LittleShort(l);}
  94. int        BigLong (int l) {return _BigLong(l);}
  95. int        LittleLong (int l) {return _LittleLong(l);}
  96. float    BigFloat (float l) {return _BigFloat(l);}
  97. float    LittleFloat (float l) {return _LittleFloat(l);}
  98.  
  99. short   ShortSwap (short l)
  100. {
  101.     byte    b1,b2;
  102.  
  103.     b1 = l&255;
  104.     b2 = (l>>8)&255;
  105.  
  106.     return (b1<<8) + b2;
  107. }
  108.  
  109. short    ShortNoSwap (short l)
  110. {
  111.     return l;
  112. }
  113.  
  114. int    LongSwap (int l)
  115. {
  116.     byte    b1,b2,b3,b4;
  117.  
  118.     b1 = l&255;
  119.     b2 = (l>>8)&255;
  120.     b3 = (l>>16)&255;
  121.     b4 = (l>>24)&255;
  122.  
  123.     return ((int)b1<<24) + ((int)b2<<16) + ((int)b3<<8) + b4;
  124. }
  125.  
  126. int    LongNoSwap (int l)
  127. {
  128.     return l;
  129. }
  130.  
  131. float FloatSwap (float f)
  132. {
  133.     union
  134.     {
  135.         float    f;
  136.         byte    b[4];
  137.     } dat1, dat2;
  138.     
  139.     
  140.     dat1.f = f;
  141.     dat2.b[0] = dat1.b[3];
  142.     dat2.b[1] = dat1.b[2];
  143.     dat2.b[2] = dat1.b[1];
  144.     dat2.b[3] = dat1.b[0];
  145.     return dat2.f;
  146. }
  147.  
  148. float FloatNoSwap (float f)
  149. {
  150.     return f;
  151. }
  152.  
  153. /*
  154. ================
  155. Swap_Init
  156. ================
  157. */
  158. void Swap_Init (void)
  159. {
  160.     byte    swaptest[2] = {1,0};
  161.  
  162. // set the byte swapping variables in a portable manner    
  163.     if ( *(short *)swaptest == 1)
  164.     {
  165.         _BigShort = ShortSwap;
  166.         _LittleShort = ShortNoSwap;
  167.         _BigLong = LongSwap;
  168.         _LittleLong = LongNoSwap;
  169.         _BigFloat = FloatSwap;
  170.         _LittleFloat = FloatNoSwap;
  171.     }
  172.     else
  173.     {
  174.         _BigShort = ShortNoSwap;
  175.         _LittleShort = ShortSwap;
  176.         _BigLong = LongNoSwap;
  177.         _LittleLong = LongSwap;
  178.         _BigFloat = FloatNoSwap;
  179.         _LittleFloat = FloatSwap;
  180.     }
  181.  
  182. }
  183.  
  184.  
  185. /*
  186. ============================================================================
  187.  
  188. PARSING
  189.  
  190. ============================================================================
  191. */
  192.  
  193. static    char    com_token[MAX_TOKEN_CHARS];
  194. static    int        com_lines;
  195.  
  196. void COM_BeginParseSession( void )
  197. {
  198.     com_lines = 0;
  199. }
  200.  
  201. int COM_GetCurrentParseLine( void )
  202. {
  203.     return com_lines;
  204. }
  205.  
  206. char *COM_Parse( char **data_p )
  207. {
  208.     return COM_ParseExt( data_p, qtrue );
  209. }
  210.  
  211.  
  212. /*
  213. ==============
  214. COM_Parse
  215.  
  216. Parse a token out of a string
  217. Will never return NULL, just empty strings
  218.  
  219. If "allowLineBreaks" is qtrue then an empty
  220. string will be returned if the next token is
  221. a newline.
  222. ==============
  223. */
  224. static char *SkipWhitespace( char *data, qboolean *hasNewLines ) {
  225.     int c;
  226.  
  227.     while( (c = *data) <= ' ') {
  228.         if( !c ) {
  229.             return NULL;
  230.         }
  231.         if( c == '\n' ) {
  232.             com_lines++;
  233.             *hasNewLines = qtrue;
  234.         }
  235.         data++;
  236.     }
  237.  
  238.     return data;
  239. }
  240.  
  241. char *COM_ParseExt( char **data_p, qboolean allowLineBreaks )
  242. {
  243.     int c = 0, len;
  244.     qboolean hasNewLines = qfalse;
  245.     char *data;
  246.  
  247.     data = *data_p;
  248.     len = 0;
  249.     com_token[0] = 0;
  250.  
  251.     // make sure incoming data is valid
  252.     if ( !data )
  253.     {
  254.         *data_p = NULL;
  255.         return com_token;
  256.     }
  257.  
  258.     while ( 1 )
  259.     {
  260.         // skip whitespace
  261.         data = SkipWhitespace( data, &hasNewLines );
  262.         if ( !data )
  263.         {
  264.             *data_p = NULL;
  265.             return com_token;
  266.         }
  267.         if ( hasNewLines && !allowLineBreaks )
  268.         {
  269.             *data_p = data;
  270.             return com_token;
  271.         }
  272.  
  273.         c = *data;
  274.  
  275.         // skip double slash comments
  276.         if ( c == '/' && data[1] == '/' )
  277.         {
  278.             while (*data && *data != '\n')
  279.                 data++;
  280.         }
  281.         // skip /* */ comments
  282.         else if ( c=='/' && data[1] == '*' ) 
  283.         {
  284.             while ( *data && ( *data != '*' || data[1] != '/' ) ) 
  285.             {
  286.                 data++;
  287.             }
  288.             if ( *data ) 
  289.             {
  290.                 data += 2;
  291.             }
  292.         }
  293.         else
  294.         {
  295.             break;
  296.         }
  297.     }
  298.  
  299.     // handle quoted strings
  300.     if (c == '\"')
  301.     {
  302.         data++;
  303.         while (1)
  304.         {
  305.             c = *data++;
  306.             if (c=='\"' || !c)
  307.             {
  308.                 com_token[len] = 0;
  309.                 *data_p = ( char * ) data;
  310.                 return com_token;
  311.             }
  312.             if (len < MAX_TOKEN_CHARS)
  313.             {
  314.                 com_token[len] = c;
  315.                 len++;
  316.             }
  317.         }
  318.     }
  319.  
  320.     // parse a regular word
  321.     do
  322.     {
  323.         if (len < MAX_TOKEN_CHARS)
  324.         {
  325.             com_token[len] = c;
  326.             len++;
  327.         }
  328.         data++;
  329.         c = *data;
  330.         if ( c == '\n' )
  331.             com_lines++;
  332.     } while (c>32);
  333.  
  334.     if (len == MAX_TOKEN_CHARS)
  335.     {
  336. //        Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS);
  337.         len = 0;
  338.     }
  339.     com_token[len] = 0;
  340.  
  341.     *data_p = ( char * ) data;
  342.     return com_token;
  343. }
  344.  
  345.  
  346. #if 0
  347. // no longer used
  348. /*
  349. ===============
  350. COM_ParseInfos
  351. ===============
  352. */
  353. int COM_ParseInfos( char *buf, int max, char infos[][MAX_INFO_STRING] ) {
  354.     char    *token;
  355.     int        count;
  356.     char    key[MAX_TOKEN_CHARS];
  357.  
  358.     count = 0;
  359.  
  360.     while ( 1 ) {
  361.         token = COM_Parse( &buf );
  362.         if ( !token[0] ) {
  363.             break;
  364.         }
  365.         if ( strcmp( token, "{" ) ) {
  366.             Com_Printf( "Missing { in info file\n" );
  367.             break;
  368.         }
  369.  
  370.         if ( count == max ) {
  371.             Com_Printf( "Max infos exceeded\n" );
  372.             break;
  373.         }
  374.  
  375.         infos[count][0] = 0;
  376.         while ( 1 ) {
  377.             token = COM_ParseExt( &buf, qtrue );
  378.             if ( !token[0] ) {
  379.                 Com_Printf( "Unexpected end of info file\n" );
  380.                 break;
  381.             }
  382.             if ( !strcmp( token, "}" ) ) {
  383.                 break;
  384.             }
  385.             Q_strncpyz( key, token, sizeof( key ) );
  386.  
  387.             token = COM_ParseExt( &buf, qfalse );
  388.             if ( !token[0] ) {
  389.                 strcpy( token, "<NULL>" );
  390.             }
  391.             Info_SetValueForKey( infos[count], key, token );
  392.         }
  393.         count++;
  394.     }
  395.  
  396.     return count;
  397. }
  398. #endif
  399.  
  400.  
  401. /*
  402. ==================
  403. COM_MatchToken
  404. ==================
  405. */
  406. void COM_MatchToken( char **buf_p, char *match ) {
  407.     char    *token;
  408.  
  409.     token = COM_Parse( buf_p );
  410.     if ( strcmp( token, match ) ) {
  411.         Com_Error( ERR_DROP, "MatchToken: %s != %s", token, match );
  412.     }
  413. }
  414.  
  415.  
  416. /*
  417. =================
  418. SkipBracedSection
  419.  
  420. The next token should be an open brace.
  421. Skips until a matching close brace is found.
  422. Internal brace depths are properly skipped.
  423. =================
  424. */
  425. void SkipBracedSection (char **program) {
  426.     char            *token;
  427.     int                depth;
  428.  
  429.     depth = 0;
  430.     do {
  431.         token = COM_ParseExt( program, qtrue );
  432.         if( token[1] == 0 ) {
  433.             if( token[0] == '{' ) {
  434.                 depth++;
  435.             }
  436.             else if( token[0] == '}' ) {
  437.                 depth--;
  438.             }
  439.         }
  440.     } while( depth && *program );
  441. }
  442.  
  443. /*
  444. =================
  445. SkipRestOfLine
  446. =================
  447. */
  448. void SkipRestOfLine ( char **data ) {
  449.     char    *p;
  450.     int        c;
  451.  
  452.     p = *data;
  453.     while ( (c = *p++) != 0 ) {
  454.         if ( c == '\n' ) {
  455.             com_lines++;
  456.             break;
  457.         }
  458.     }
  459.  
  460.     *data = p;
  461. }
  462.  
  463.  
  464. void Parse1DMatrix (char **buf_p, int x, float *m) {
  465.     char    *token;
  466.     int        i;
  467.  
  468.     COM_MatchToken( buf_p, "(" );
  469.  
  470.     for (i = 0 ; i < x ; i++) {
  471.         token = COM_Parse(buf_p);
  472.         m[i] = atof(token);
  473.     }
  474.  
  475.     COM_MatchToken( buf_p, ")" );
  476. }
  477.  
  478. void Parse2DMatrix (char **buf_p, int y, int x, float *m) {
  479.     int        i;
  480.  
  481.     COM_MatchToken( buf_p, "(" );
  482.  
  483.     for (i = 0 ; i < y ; i++) {
  484.         Parse1DMatrix (buf_p, x, m + i * x);
  485.     }
  486.  
  487.     COM_MatchToken( buf_p, ")" );
  488. }
  489.  
  490. void Parse3DMatrix (char **buf_p, int z, int y, int x, float *m) {
  491.     int        i;
  492.  
  493.     COM_MatchToken( buf_p, "(" );
  494.  
  495.     for (i = 0 ; i < z ; i++) {
  496.         Parse2DMatrix (buf_p, y, x, m + i * x*y);
  497.     }
  498.  
  499.     COM_MatchToken( buf_p, ")" );
  500. }
  501.  
  502.  
  503. /*
  504. ============================================================================
  505.  
  506.                     LIBRARY REPLACEMENT FUNCTIONS
  507.  
  508. ============================================================================
  509. */
  510.  
  511. int Q_isprint( int c )
  512. {
  513.     if ( c >= 0x20 && c <= 0x7E )
  514.         return ( 1 );
  515.     return ( 0 );
  516. }
  517.  
  518. int Q_islower( int c )
  519. {
  520.     if (c >= 'a' && c <= 'z')
  521.         return ( 1 );
  522.     return ( 0 );
  523. }
  524.  
  525. int Q_isupper( int c )
  526. {
  527.     if (c >= 'A' && c <= 'Z')
  528.         return ( 1 );
  529.     return ( 0 );
  530. }
  531.  
  532. int Q_isalpha( int c )
  533. {
  534.     if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))
  535.         return ( 1 );
  536.     return ( 0 );
  537. }
  538.  
  539. char* Q_strrchr( const char* string, int c )
  540. {
  541.     char cc = c;
  542.     char *s;
  543.     char *sp=(char *)0;
  544.  
  545.     s = (char*)string;
  546.  
  547.     while (*s)
  548.     {
  549.         if (*s == cc)
  550.             sp = s;
  551.         s++;
  552.     }
  553.     if (cc == 0)
  554.         sp = s;
  555.  
  556.     return sp;
  557. }
  558.  
  559. /*
  560. =============
  561. Q_strncpyz
  562.  
  563. Safe strncpy that ensures a trailing zero
  564. =============
  565. */
  566. void Q_strncpyz( char *dest, const char *src, int destsize ) {
  567.     if ( !src ) {
  568.         Com_Error( ERR_FATAL, "Q_strncpyz: NULL src" );
  569.     }
  570.     if ( destsize < 1 ) {
  571.         Com_Error(ERR_FATAL,"Q_strncpyz: destsize < 1" ); 
  572.     }
  573.  
  574.     strncpy( dest, src, destsize-1 );
  575.     dest[destsize-1] = 0;
  576. }
  577.                  
  578. int Q_stricmpn (const char *s1, const char *s2, int n) {
  579.     int        c1, c2;
  580.     
  581.     do {
  582.         c1 = *s1++;
  583.         c2 = *s2++;
  584.  
  585.         if (!n--) {
  586.             return 0;        // strings are equal until end point
  587.         }
  588.         
  589.         if (c1 != c2) {
  590.             if (c1 >= 'a' && c1 <= 'z') {
  591.                 c1 -= ('a' - 'A');
  592.             }
  593.             if (c2 >= 'a' && c2 <= 'z') {
  594.                 c2 -= ('a' - 'A');
  595.             }
  596.             if (c1 != c2) {
  597.                 return c1 < c2 ? -1 : 1;
  598.             }
  599.         }
  600.     } while (c1);
  601.     
  602.     return 0;        // strings are equal
  603. }
  604.  
  605. int Q_strncmp (const char *s1, const char *s2, int n) {
  606.     int        c1, c2;
  607.     
  608.     do {
  609.         c1 = *s1++;
  610.         c2 = *s2++;
  611.  
  612.         if (!n--) {
  613.             return 0;        // strings are equal until end point
  614.         }
  615.         
  616.         if (c1 != c2) {
  617.             return c1 < c2 ? -1 : 1;
  618.         }
  619.     } while (c1);
  620.     
  621.     return 0;        // strings are equal
  622. }
  623.  
  624. int Q_stricmp (const char *s1, const char *s2) {
  625.     return Q_stricmpn (s1, s2, 99999);
  626. }
  627.  
  628.  
  629. char *Q_strlwr( char *s1 ) {
  630.     char    *s;
  631.  
  632.     s = s1;
  633.     while ( *s ) {
  634.         *s = tolower(*s);
  635.         s++;
  636.     }
  637.     return s1;
  638. }
  639.  
  640. char *Q_strupr( char *s1 ) {
  641.     char    *s;
  642.  
  643.     s = s1;
  644.     while ( *s ) {
  645.         *s = toupper(*s);
  646.         s++;
  647.     }
  648.     return s1;
  649. }
  650.  
  651.  
  652. // never goes past bounds or leaves without a terminating 0
  653. void Q_strcat( char *dest, int size, const char *src ) {
  654.     int        l1;
  655.  
  656.     l1 = strlen( dest );
  657.     if ( l1 >= size ) {
  658.         Com_Error( ERR_FATAL, "Q_strcat: already overflowed" );
  659.     }
  660.     Q_strncpyz( dest + l1, src, size - l1 );
  661. }
  662.  
  663.  
  664. int Q_PrintStrlen( const char *string ) {
  665.     int            len;
  666.     const char    *p;
  667.  
  668.     if( !string ) {
  669.         return 0;
  670.     }
  671.  
  672.     len = 0;
  673.     p = string;
  674.     while( *p ) {
  675.         if( Q_IsColorString( p ) ) {
  676.             p += 2;
  677.             continue;
  678.         }
  679.         p++;
  680.         len++;
  681.     }
  682.  
  683.     return len;
  684. }
  685.  
  686.  
  687. char *Q_CleanStr( char *string ) {
  688.     char*    d;
  689.     char*    s;
  690.     int        c;
  691.  
  692.     s = string;
  693.     d = string;
  694.     while ((c = *s) != 0 ) {
  695.         if ( Q_IsColorString( s ) ) {
  696.             s++;
  697.         }        
  698.         else if ( c >= 0x20 && c <= 0x7E ) {
  699.             *d++ = c;
  700.         }
  701.         s++;
  702.     }
  703.     *d = '\0';
  704.  
  705.     return string;
  706. }
  707.  
  708.  
  709. void QDECL Com_sprintf( char *dest, int size, const char *fmt, ...) {
  710.     int        len;
  711.     va_list        argptr;
  712.     char    bigbuffer[32000];    // big, but small enough to fit in PPC stack
  713.  
  714.     va_start (argptr,fmt);
  715.     len = vsprintf (bigbuffer,fmt,argptr);
  716.     va_end (argptr);
  717.     if ( len >= sizeof( bigbuffer ) ) {
  718.         Com_Error( ERR_FATAL, "Com_sprintf: overflowed bigbuffer" );
  719.     }
  720.     if (len >= size) {
  721.         Com_Printf ("Com_sprintf: overflow of %i in %i\n", len, size);
  722.     }
  723.     Q_strncpyz (dest, bigbuffer, size );
  724. }
  725.  
  726.  
  727. /*
  728. ============
  729. va
  730.  
  731. does a varargs printf into a temp buffer, so I don't need to have
  732. varargs versions of all text functions.
  733. FIXME: make this buffer size safe someday
  734. ============
  735. */
  736. char    * QDECL va( char *format, ... ) {
  737.     va_list        argptr;
  738.     static char        string[2][32000];    // in case va is called by nested functions
  739.     static int        index = 0;
  740.     char    *buf;
  741.  
  742.     buf = string[index & 1];
  743.     index++;
  744.  
  745.     va_start (argptr, format);
  746.     vsprintf (buf, format,argptr);
  747.     va_end (argptr);
  748.  
  749.     return buf;
  750. }
  751.  
  752.  
  753. /*
  754. =====================================================================
  755.  
  756.   INFO STRINGS
  757.  
  758. =====================================================================
  759. */
  760.  
  761. /*
  762. ===============
  763. Info_ValueForKey
  764.  
  765. Searches the string for the given
  766. key and returns the associated value, or an empty string.
  767. FIXME: overflow check?
  768. ===============
  769. */
  770. char *Info_ValueForKey( const char *s, const char *key ) {
  771.     char    pkey[MAX_INFO_KEY];
  772.     static    char value[2][MAX_INFO_VALUE];    // use two buffers so compares
  773.                                             // work without stomping on each other
  774.     static    int    valueindex = 0;
  775.     char    *o;
  776.     
  777.     if ( !s || !key ) {
  778.         return "";
  779.     }
  780.  
  781.     if ( strlen( s ) >= MAX_INFO_STRING ) {
  782.         Com_Error( ERR_DROP, "Info_ValueForKey: oversize infostring" );
  783.     }
  784.  
  785.     valueindex ^= 1;
  786.     if (*s == '\\')
  787.         s++;
  788.     while (1)
  789.     {
  790.         o = pkey;
  791.         while (*s != '\\')
  792.         {
  793.             if (!*s)
  794.                 return "";
  795.             *o++ = *s++;
  796.         }
  797.         *o = 0;
  798.         s++;
  799.  
  800.         o = value[valueindex];
  801.  
  802.         while (*s != '\\' && *s)
  803.         {
  804.             *o++ = *s++;
  805.         }
  806.         *o = 0;
  807.  
  808.         if (!Q_stricmp (key, pkey) )
  809.             return value[valueindex];
  810.  
  811.         if (!*s)
  812.             break;
  813.         s++;
  814.     }
  815.  
  816.     return "";
  817. }
  818.  
  819.  
  820. /*
  821. ===================
  822. Info_NextPair
  823.  
  824. Used to itterate through all the key/value pairs in an info string
  825. ===================
  826. */
  827. void Info_NextPair( const char **head, char key[MAX_INFO_KEY], char value[MAX_INFO_VALUE] ) {
  828.     char    *o;
  829.     const char    *s;
  830.  
  831.     s = *head;
  832.  
  833.     if ( *s == '\\' ) {
  834.         s++;
  835.     }
  836.     key[0] = 0;
  837.     value[0] = 0;
  838.  
  839.     o = key;
  840.     while ( *s != '\\' ) {
  841.         if ( !*s ) {
  842.             *o = 0;
  843.             *head = s;
  844.             return;
  845.         }
  846.         *o++ = *s++;
  847.     }
  848.     *o = 0;
  849.     s++;
  850.  
  851.     o = value;
  852.     while ( *s != '\\' && *s ) {
  853.         *o++ = *s++;
  854.     }
  855.     *o = 0;
  856.  
  857.     *head = s;
  858. }
  859.  
  860.  
  861. /*
  862. ===================
  863. Info_RemoveKey
  864. ===================
  865. */
  866. void Info_RemoveKey( char *s, const char *key ) {
  867.     char    *start;
  868.     char    pkey[MAX_INFO_KEY];
  869.     char    value[MAX_INFO_VALUE];
  870.     char    *o;
  871.  
  872.     if ( strlen( s ) >= MAX_INFO_STRING ) {
  873.         Com_Error( ERR_DROP, "Info_RemoveKey: oversize infostring" );
  874.     }
  875.  
  876.     if (strchr (key, '\\')) {
  877.         return;
  878.     }
  879.  
  880.     while (1)
  881.     {
  882.         start = s;
  883.         if (*s == '\\')
  884.             s++;
  885.         o = pkey;
  886.         while (*s != '\\')
  887.         {
  888.             if (!*s)
  889.                 return;
  890.             *o++ = *s++;
  891.         }
  892.         *o = 0;
  893.         s++;
  894.  
  895.         o = value;
  896.         while (*s != '\\' && *s)
  897.         {
  898.             if (!*s)
  899.                 return;
  900.             *o++ = *s++;
  901.         }
  902.         *o = 0;
  903.  
  904.         if (!strcmp (key, pkey) )
  905.         {
  906.             strcpy (start, s);    // remove this part
  907.             return;
  908.         }
  909.  
  910.         if (!*s)
  911.             return;
  912.     }
  913.  
  914. }
  915.  
  916.  
  917. /*
  918. ==================
  919. Info_Validate
  920.  
  921. Some characters are illegal in info strings because they
  922. can mess up the server's parsing
  923. ==================
  924. */
  925. qboolean Info_Validate( const char *s ) {
  926.     if ( strchr( s, '\"' ) ) {
  927.         return qfalse;
  928.     }
  929.     if ( strchr( s, ';' ) ) {
  930.         return qfalse;
  931.     }
  932.     return qtrue;
  933. }
  934.  
  935. /*
  936. ==================
  937. Info_SetValueForKey
  938.  
  939. Changes or adds a key/value pair
  940. ==================
  941. */
  942. void Info_SetValueForKey( char *s, const char *key, const char *value ) {
  943.     char    newi[MAX_INFO_STRING];
  944.  
  945.     if ( strlen( s ) >= MAX_INFO_STRING ) {
  946.         Com_Error( ERR_DROP, "Info_SetValueForKey: oversize infostring" );
  947.     }
  948.  
  949.     if (strchr (key, '\\') || strchr (value, '\\'))
  950.     {
  951.         Com_Printf ("Can't use keys or values with a \\\n");
  952.         return;
  953.     }
  954.  
  955.     if (strchr (key, ';') || strchr (value, ';'))
  956.     {
  957.         Com_Printf ("Can't use keys or values with a semicolon\n");
  958.         return;
  959.     }
  960.  
  961.     if (strchr (key, '\"') || strchr (value, '\"'))
  962.     {
  963.         Com_Printf ("Can't use keys or values with a \"\n");
  964.         return;
  965.     }
  966.  
  967.     Info_RemoveKey (s, key);
  968.     if (!value || !strlen(value))
  969.         return;
  970.  
  971.     Com_sprintf (newi, sizeof(newi), "\\%s\\%s", key, value);
  972.  
  973.     if (strlen(newi) + strlen(s) > MAX_INFO_STRING)
  974.     {
  975.         Com_Printf ("Info string length exceeded\n");
  976.         return;
  977.     }
  978.  
  979.     strcat (s, newi);
  980. }
  981.  
  982. //====================================================================
  983.  
  984.  
  985.